package reservation;

import java.io.IOException;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;

import org.jdom.Element;

import serialisation.SerializableText;
import serialisation.SerializableXML;
import algorithme.GraphePoids;
import data.Ville;
import errors.NegativeValueException;
import errors.StartStopException;

/**
 * Classe trajet : modélisée par une ville de départ, une destination, une date
 * et un type de transport
 * 
 * @author Alexandre Boursier and Mamadou Diallo and Cyril Gorrieri and Emmanuel
 *         Menage
 * @version 2012.01.23
 */

public class Trajet implements Serializable, SerializableText, SerializableXML {

    private static final long serialVersionUID = 1386487821270813499L;

    /**
     * Ville de départ
     */
    private Ville depart;

    /**
     * Ville d'arrivée
     */
    private Ville arrivee;

    /**
     * Date du trajet
     */
    private Date date;

    /**
     * Relation de composition : un trajet donné est effectué par un transport
     * donné
     */
    private Transport transport;

    /**
     * Liste des participants au voyage
     */
    private List<Billet> billets;

    /**
     * static qui permet d'incrementer l'ident transport automatiquement
     */
    protected static int ident = 0;
    
    public static int getIdent() {
		return ident;
	}
	
	public static void setIdent(int i) {
		ident = i;
	}

    /**
     * Identifiant du transport
     */
    private int identTrajet;

    /**
     * Créer un trajet selon quatre paramètres
     * 
     * @param start
     *            La ville de départ.
     * @param stop
     *            La ville d'arrivée.
     * @param now
     *            La date à laquelle le trajet aura lieu.
     * @param commute
     *            Le transport utilisé pour le trajet.
     * @param clients
     *            L'ensemble des clients qui ont reservé une place dans ce
     *            trajet
     * @throws StartStopException
     * @throws IOException
     * @throws NullPointerException
     * @throws NegativeValueException
     * @throws NumberFormatException
     * @throws ParseException
     */
    public Trajet(Ville start, Ville stop, Date now, Transport commute)
            throws StartStopException, ParseException {
        if (start.equals(stop))
            throw new StartStopException(start);
        this.depart = start;
        this.arrivee = stop;
        this.date = now;
        this.transport = commute;
        this.billets = new ArrayList<Billet>();
        this.identTrajet = Trajet.ident;
        Trajet.ident++;
    }

    /**
     * Contructeur de copie
     * 
     * @param t Le trajet à copier
     */
    public Trajet(Trajet t) {
        this.depart = t.depart;
        this.arrivee = t.arrivee;
        this.date = t.date;
        this.transport = t.transport;
        this.identTrajet = Trajet.ident;
        this.billets = t.billets;
        Trajet.ident++;
    }

    /**
     * Créer un trajet existant dans la Base de données (XML)
     * 
     * @param identifiant
     *            Identifiant du trajet
     * @param start
     *            La ville de départ.
     * @param stop
     *            La ville d'arrivée.
     * @param now
     *            La date à laquelle le trajet aura lieu.
     * @param commute
     *            Le transport utilisé pour le trajet.
     * @param clients
     *            L'ensemble des clients qui ont reservé une place dans ce
     *            trajet
     * @throws ParseException
     * @throws StartStopException
     * @throws NegativeValueException
     * @throws IOException
     * @throws NullPointerException
     * @throws NumberFormatException
     */
    public Trajet(int identifiant, Ville start, Ville stop, Date now,
            Transport commute) throws ParseException {
        this.identTrajet = identifiant;
        if (Trajet.ident <= identifiant)
            Trajet.ident = identifiant + 1;
        this.depart = start;
        this.arrivee = stop;
        this.date = now;
        this.transport = commute;
        this.billets = new ArrayList<Billet>();
    }

    /**
     * @return la ville de départ
     */
    public Ville getDepart() {
        return depart;
    }

    /**
     * @param la
     *            ville de départ à modifier
     * @throws StartStopException
     * @throws NegativeValueException
     * @throws IOException
     * @throws NullPointerException
     * @throws NumberFormatException
     */
    public void setDepart(Ville depart) throws NumberFormatException,
    NullPointerException, IOException, NegativeValueException,
    StartStopException {
        if (depart.equals(arrivee))
            throw new StartStopException(depart);
        this.depart = depart;
    }

    /**
     * @return la ville d'arrivée
     */
    public Ville getArrivee() {
        return arrivee;
    }

    /**
     * @param le
     *            ville d'arrivée à modifier
     * @throws StartStopException
     * @throws NegativeValueException
     * @throws IOException
     * @throws NullPointerException
     * @throws NumberFormatException
     */
    public void setArrivee(Ville arrivee) throws NumberFormatException,
    NullPointerException, IOException, NegativeValueException,
    StartStopException {
        if (arrivee.equals(depart))
            throw new StartStopException(depart);
        this.arrivee = arrivee;
    }

    /**
     * @return la date du trajet
     */
    public String getDate() {
        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
        return dateFormat.format(date);
    }
    
    /**
     * @return la date du trajet
     */
    public String getDate(Date d) {
        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
        return dateFormat.format(d);
    }

    /**
     * @param la
     *            date du trajet à modifier
     * @throws ParseException
     * @throws StartStopException
     * @throws NegativeValueException
     * @throws IOException
     * @throws NullPointerException
     * @throws NumberFormatException
     */
    public void setDate(Date s) throws ParseException, NumberFormatException,
    NullPointerException, IOException, NegativeValueException,
    StartStopException {
        this.date = s;
    }

    /**
     * Convertir en date
     * 
     * @param s
     *            La chaîne de caractères cible
     * @throws ParseException
     */
    public void toDate(String s) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
        this.date = sdf.parse(s);
    }

    /**
     * Convertir en date
     * 
     * @param s
     *            La chaîne de caractères cible
     * @throws ParseException
     */
    public static Date toFormatedDate(String s) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
        return sdf.parse(s);
    }

    /**
     * @return le transport cible
     */
    public Transport getTransport() {
        return transport;
    }

    /**
     * @param le
     *            transport cible à modifier
     * @throws StartStopException
     * @throws NegativeValueException
     * @throws IOException
     * @throws NullPointerException
     * @throws NumberFormatException
     */
    public void setTransport(Transport transport) throws NumberFormatException,
    NullPointerException, IOException, NegativeValueException,
    StartStopException {
        this.transport = transport;
    }

    /**
     * @return the identTrajet
     */
    public int getIdentTrajet() {
        return identTrajet;
    }

    /**
     * @return les voyageurs d'un trajet donné
     */
    public List<Billet> getBillet() {
        return billets;
    }

    /**
     * Ajouter un client dans la liste des voyageurs
     * 
     * @param c
     *            Le client à ajouter
     */
    public boolean addBillet(Billet c) {
        return billets.add(c);
    }
    
    public boolean deleteBillet(Billet b) {
    	// Parcourir la liste des billets
    	for (int j = 0; j < billets.size(); j++) {
    		Billet bi = billets.get(j);
    		// Vérifier que c'est le bon client et le bon nom
    		if (bi.getClient().equals(b.getClient()) && bi.getNom().equals(b.getNom())) {
    			// Supprimer le client de la liste
    			billets.remove(j);
    			return true;
    		}
    	}
    	return false;
    }

    /**
     * Détermine si un participant existe déjà ou non
     * 
     * @param client
     *            Client cible
     * @return True si le client existe déjà, false sinon
     */
    public boolean exists(Client C) {
        Iterator<Billet> iter = billets.iterator();
        while (iter.hasNext()) {
            if (iter.next().getClient().getLogin().equals(C.getLogin()))
                return true;
        }
        return false;
    }

    /**
     * 
     * @return le nombre de places restantes
     */
    public int getPlacesRestantes() {
        return transport.getCapacite() - billets.size();
    }

    /**
     * 
     * @return true si il reste des places, false sinon
     */
    public boolean existPlaces() {
        if (getPlacesRestantes() > 0)
            return true;
        else
            return false;
    }

    /**
     * toString()
     * 
     * @return string contenant la liste des trajets
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("#######################################################\n");
        sb.append("Trajet n°" + identTrajet + "\n");
        sb.append("Départ : " + depart + "\n");
        sb.append("Arrivée : " + arrivee + "\n");
        sb.append("Date : " + this.getDate() + "\n");
        sb.append(transport.toString());

        if (this.getPlacesRestantes() == 0)
            sb.append("Il ne reste plus de places disponibles.\n");
        else
            sb.append("Nombre de places restantes : "
                    + this.getPlacesRestantes() + "\n");

        if (billets.size() > 0) {
            sb.append("Liste des voyageurs : \n");
            for (Billet c : billets) {
                sb.append("\t");
                sb.append(c.getClient().toString());
            }
        } else {
            sb.append("Pas de voyageur associé au trajet actuellement \n");
        }
        sb.append("#######################################################");
        return sb.toString();
    }

    /**
     * Surcharge de la méthode equals qui sera utilisé dans une liste lorsque la
     * methode Liste.contains(Objet) est appellée
     * 
     * @param t
     *            L'objet cible
     * @return true si l'identifiant referant de l'objet est le même, false
     *         sinon
     */
    @Override
    public boolean equals(Object t) {
        Trajet tr = (Trajet) t;
        if (tr.getIdentTrajet() == identTrajet)
            return true;
        return false;
    }

    /** 
     * Calcule le prix de base d'un trajet, qui est lié au moyen de transport utilisé.
     * 
     * @retrun le prix de base d'un trajet.
     */
    public int calculerPrix() {
        return  (int)(transport.getCategorie().getPrix()
                *GraphePoids.getDistVilles(this.getDepart().getRang(), this.getArrivee().getRang()));
    }

    /**
     * Calculer le temps nécessaire pour aller de la ville de départ à la ville d'arrivée
     * selon un transport donné
     * @return le temps nécessaire
     */
    public double getTemps() {
        return (GraphePoids.getDistVilles(this.getDepart().getRang(), this.getArrivee().getRang())
                /transport.getCategorie().getVitesse())*60;
    }
    
    public String getTempsInHours() {
        int res = (int)getTemps();
        int i = 0;
        while(res >= 60)
        {
            i++;
            res -= 60;
        }
        if(res == 0)
            return new String(i+" heures");
        if (i == 1 && res == 1)
            return new String(i+" heure et "+res+" minute");
        if (i == 1)
            return new String(i+" heure et "+res+" minutes");
        return new String(i+" heures et "+res+" minutes");

    }
    
    public String calculerArrivee()
    {
        GregorianCalendar d = new GregorianCalendar();
        d.setTime(date);
        d.add(GregorianCalendar.MINUTE, (int)getTemps());
        return getDate(d.getTime());
    }
    
    @Override
    public List<String> parseText() {
        List<String> attributes = new ArrayList<String>();
        attributes.add("TRAJET");
        attributes.add(String.valueOf(identTrajet));
        attributes.add(String.valueOf(depart));
        attributes.add(String.valueOf(arrivee));
        attributes.add(this.getDate());
        attributes.add(String.valueOf(transport.getIdentTransport()));
        return attributes;
    }

	@Override
	public Element parseXML() {
		Element trajet = new Element("trajet");

		// Créer l'élément correspondant a l'identifiant du trajet
		Element idTrajetE = new Element("idTrajet");
		idTrajetE.setText(new Integer(identTrajet).toString());

		// Ajouter l'identifiant du trajet à l'élément trajet
		trajet.addContent(idTrajetE);

		// Créer l'élément correspondant au départ
		Element departE = new Element("depart");
		departE.setText(depart.toString());

		// Ajouter le départ à l'élement trajet
		trajet.addContent(departE);

		// Créer l'élément correspondant au départ
		Element arriveeE = new Element("arrivee");
		arriveeE.setText(arrivee.toString());

		// Ajouter le départ à l'élement trajet
		trajet.addContent(arriveeE);

		// Créer l'élément correspondant à la date
		Element dateE = new Element("date");
		dateE.setText(getDate());

		// Ajouter la date à l'élement trajet
		trajet.addContent(dateE);

		// Créer l'élément correspondant au transport employé pour le trajet
		Element typeTransportE = new Element("transportAssocie");
		typeTransportE.setText(new Integer(transport.getIdentTransport())
				.toString());

		// Ajouter le type de transport à l'élement trajet
		trajet.addContent(typeTransportE);
		
		/* On ajoute la balise pour les billets */
		Element billetsE = new Element("billets");
		trajet.addContent(billetsE);
		for(Billet b : billets) {
			//Créer l'élément correspondant
			billetsE.addContent(b.parseXML());
		}
		return trajet;
	}
}